package com.smartrodin.springbootrabbitmq.aop;

import com.smartrodin.springbootrabbitmq.annotation.RedisCache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

/**
 * @Author: wanger
 * @Date: Created in 2019/8/3 22:36
 * @description: 处理缓存
 * 因为集成 springboot controller 返回值的问题，需要在 service 层处理，后期迁移，或者使用 jedis 连接
 */
@Aspect
@Component
public class RedisCacheAop {

    private static final String STR_REFIX = "#";
    @Autowired
    private RedisTemplate redisTemplate;

    @Around("@annotation(com.smartrodin.springbootrabbitmq.annotation.RedisCache)")
    public Object queruCache(ProceedingJoinPoint joinPoint) throws Throwable {
        String redisKey = "";
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = joinPoint.getTarget().getClass().getMethod( signature.getName(), signature.getMethod().getParameterTypes() );
        RedisCache redisCache = method.getAnnotation( RedisCache.class );
        // 获取当前运行的这个方法上面注解中的 key 内容, 如果字符串首字母不是 '#' 开头的,则将该字符串当 redisKey 使用,为空使用类名 + 方法名为 redisKey
        String elKey = redisCache.key();
        if (StringUtils.isEmpty( elKey )) {
            redisKey = joinPoint.getTarget().getClass().getName() + "." + signature.getName();
        } else if (!elKey.startsWith( STR_REFIX )) {
            redisKey = elKey;
        } else {
            // 1. 创建解析器
            ExpressionParser parser = new SpelExpressionParser();
            Expression expression = parser.parseExpression( elKey );

            // 2.设置解析上下文（有哪些占位符， 以及每种占位符的值） // 参数
            EvaluationContext context = new StandardEvaluationContext();
            // 真实参数名（运行时获取的参数名是 arg0, arg1）
            Object[] args = joinPoint.getArgs();
            DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
            String[] parameterNames = discoverer.getParameterNames( method );
            for (int i = 0; i < parameterNames.length; i++) {
                // 变量的值来自于方法上的参数
                context.setVariable( parameterNames[i], args[i] );
            }
            // 3.解析
            redisKey = expression.getValue( context ).toString();
        }

        int elExpire = redisCache.expire();
        Object result = redisTemplate.opsForValue().get( redisKey );
        if (null == result) {
            // 执行方法
            result = joinPoint.proceed();
            redisTemplate.opsForValue().set( redisKey, result, elExpire );
        }
        return result;
    }
}
